// Proxy 的拦截方法
new Proxy(
  {},
  {
    set() {},
    get() {},
    has() {}, // in
    deleteProperty() {}, // delete
    ownKeys() {}, // Object.keys() Object.getOwnPropertyNames() Object.getOwnPropertySymbols() for...in
    getOwnPropertyDescriptor() {}, // Object.getOwnPropertyDescriptor()
    defineProperty() {},
    setPrototypeOf() {},
    getPrototypeOf() {},
    preventExtensions() {},
    isExtensible() {},
    apply() {},
    construct() {},
  }
)

// 用 get 拦截器，实现函数的连续调用，实现函数柯里化
var pipe = function (value) {
  var funcStack = []
  var oproxy = new Proxy(
    {},
    {
      get: function (pipeObject, fnName) {
        if (fnName === 'get') {
          // return 并不会阻止后续的循环和函数执行
          return funcStack.reduce(function (val, fn) {
            return fn(val)
          }, value) // 有初始值的话，回调的第一个参数就是初始值，第二个参数为数组的第一个元素
          // reduce 的 第一个参数是 acc 第二个参数是当前数组元素
          // 这个也是函数柯里化的思想
        }
        funcStack.push(window[fnName])
        return oproxy
      },
    }
  )

  return oproxy
}

var double = (n) => n * 2
var pow = (n) => n * n
var reverseInt = (n) => n.toString().split('').reverse().join('') | 0

pipe(3).double.pow.reverseInt.get // 63

// 用get拦截器 生成dom的方法
const dom = new Proxy(
  {},
  {
    get(target, property) {
      return function (attrs = {}, ...children) {
        const el = document.createElement(property)
        for (let prop of Object.keys(attrs)) {
          el.setAttribute(prop, attrs[prop])
        }
        for (let child of children) {
          if (typeof child === 'string') {
            child = document.createTextNode(child)
          }
          el.appendChild(child)
        }
        return el
      }
    },
  }
)

const el = dom.div(
  {},
  'Hello, my name is ',
  dom.a({ href: '//example.com' }, 'Mark'),
  '. I like:',
  dom.ul(
    {},
    dom.li({}, 'The web'),
    dom.li({}, 'Food'),
    dom.li({}, "…actually that's it")
  )
)

document.body.appendChild(el)

// 利用set方法，还可以数据绑定，即每当对象发生变化时，会自动更新 DOM。

// Proxy 的 getPrototypeOf 拦截
// Object.prototype.__proto__
// Object.prototype.isPrototypeOf()
// Object.getPrototypeOf()
// Reflect.getPrototypeOf()
// instanceof

// Proxy 的 ownKeys() 拦截  这个拦截器的细节有点多
// Object.getOwnPropertyNames()
// Object.getOwnPropertySymbols()
// Object.keys()
// for...in
// Reflect.ownKeys()

// Proxy 的 静态方法
Proxy.revocable()
// Proxy.revocable()的一个使用场景是，目标对象不允许直接访问，必须通过代理访问，一旦访问结束，就收回代理权，不允许再次访问。

// Proxy 的 this 问题
// 1. 在 Proxy 代理的情况下，目标对象内部的this关键字会指向 Proxy 代理。
// 2. Proxy 拦截函数内部的this，指向的是handler对象
